import os
import sys
import types
import re
from mediadart import log
from json import dumps, loads
from twisted.internet import reactor, defer
from twisted.web import resource, server, error
from mediadart import log
from mediadart.config import Configurator
from mediadart.mqueue.mqclient_twisted import Proxy
from mediadart.mqueue.mqserver import doHTTP

class ProxyError(Exception): pass

class WebProxy(resource.Resource):
    isLeaf = True
    _conf = Configurator()

    def __init__(self):
        resource.Resource.__init__(self)
        c = Configurator()
        self._configdata = {'host':c.get('MQUEUE', 'broker_ip'),
                      'port':c.getint('MQUEUE', 'broker_port'),
                      'username':c.get('MQUEUE', 'username'),
                      'password':c.get('MQUEUE', 'password'),
                     }
        self.re_name = re.compile('^\w+$')

    def check_server_name(self, name):
        "check that name is made up of letters and underscores only"
        if self.re_name.match(name):
            return name
        else:
            raise ProxyError('Invalid server name')

    def http_response(self, request, code, body):
        "The HTTP part of the response"
        request.setResponseCode(code[0], code[1])
        request.setHeader("content-length", str(len(body)))
        request.write(body)
        request.finish()

    def json_response(self, req_id, result=None, error=None):
        "The JSON-RPC part of the response"
        try:
            ret = dumps({'id':req_id, 'result':result, 'error':error})
        except TypeError, e:
            if error != None:   # error can be a python exception
                try:
                    ret = dumps({'id':req_id, 'result':str(result), 'error':str(error)})
                except TypeError, e:
                    raise(ProxyError('Unable to encode response: %s' % str(e)))
        return ret

    def render_POST(self, request):
        log.debug('Received:\n%s' % request.content.getvalue())
        log.debug('Postpath = %s' % request.postpath)
        request.setHeader("content-type", "text/html")
        server_name = self.check_server_name(request.postpath[0])
        req_id = 'undefined'
        try:
            data = loads(request.content.getvalue())
            req_id = data['id']
            method_name = data['method']
            args = data['params']
            callback_url = str(data.get('callback_url', ''))
        except Exception, e:
            self.http_response(request, (200, 'OK'), 
                        self.json_response(req_id, error='Unable to parse json request: %s' % str(e)))

        try:
            proxy = Proxy(server_name, **self._configdata)
            m = getattr(proxy, method_name)
            if type(args) == types.ListType:
                d = m(*args)
            elif type(args) == types.DictType:
                d = m(**dict([(str(x), args[x]) for x in args]))
            else:
                raise ProxyError('Unrecognized type of argument: %s' % str(type(args)))
        except Exception, e:
            self.http_response(request, (200, 'OK'), 
                                self.json_response(req_id, error='Bad Request: %s' % str(e)))

        d.addCallbacks(self._cbRender, self._cbRender, [request, req_id, True, callback_url], None, 
                                                       [request, req_id, False,callback_url], None)
        if not callback_url:
            return server.NOT_DONE_YET
        else:
            self.http_response(request, (200, 'OK'), self.json_response(req_id, 'Result sent to %s' % callback_url))

    def _cbRender(self, result, request, req_id, ok, callback_url):
        if ok:
            response = self.json_response(req_id, result=result)
        else:
            response = self.json_response(req_id, error=result)

        if not callback_url:   # synchronous call
            self.http_response(request, (200, 'OK'), response)
        else:
            factory = doHTTP(callback_url, response)
            factory.deferred.addCallbacks(self._log_ok, self._log_error)
            self.http_response(request, (200, 'OK'), response)

    def _log_ok(self, page):
        log.debug('ok sending page')
        
    def _log_error(self, reason):
        log.debug('error sending page: %s' % reason)


def start_server(root):
    c = Configurator()
    path = c.get('WWW', 'proxy_path')
    root.putChild(path, WebProxy())

if __name__ == '__main__':
    if len(sys.argv)==1:
        port = 8081
    else:
        port = int(sys.argv[1])
    log.warning('starting upload server on port %d' % port)
    r = resource.Resource()
    reactor.listenTCP(int(port), server.Site(r))
    start_server(r)
    reactor.run()
